Chapter 1: Why React? (And Why You Might Not Need It)
The JavaScript framework landscape in 2025
The JavaScript Framework Landscape in 2025
Before we dive into React, let's acknowledge the elephant in the room: the JavaScript ecosystem is overwhelming. New frameworks appear monthly, each claiming to solve problems you didn't know you had. By the time you finish reading this chapter, three new meta-frameworks will have launched on Twitter.
But here's the reality: the framework wars are over, and React won. Not because it's perfect—it's not. Not because it's the fastest—it isn't. React won because it became the default choice, and defaults are powerful.
The Current State of Play
As of 2025, the frontend landscape has consolidated around a few major players:
React (2013-present): The incumbent. 40%+ of all frontend jobs. Massive ecosystem. Backed by Meta. You're reading this book because React is the safe, pragmatic choice for most projects.
Vue (2014-present): The elegant alternative. Easier learning curve than React. Popular in Asia and among solo developers. Excellent documentation. Smaller job market.
Svelte (2016-present): The compiler-based upstart. No virtual DOM. Writes less code. Growing rapidly but still niche. SvelteKit (its Next.js equivalent) is maturing.
Angular (2016-present): The enterprise framework. TypeScript-first. Opinionated. Declining but still dominant in large corporations and government projects.
Solid (2021-present): The performance obsessive. React-like syntax but fundamentally different reactivity model. Tiny but passionate community.
Qwik (2021-present): The resumability pioneer. Focuses on instant interactivity. Interesting ideas, uncertain future.
What This Means for You
If you're learning frontend development in 2025, React is the most pragmatic choice for these reasons:
- Job market dominance: 4 out of 10 frontend job postings require React
- Ecosystem maturity: Every problem you'll face has a React solution
- Knowledge transferability: React patterns appear in other frameworks
- Next.js: The killer app that makes React viable for production
- Community size: More tutorials, more Stack Overflow answers, more help
But—and this is important—React is not always the right choice. We'll explore when to choose alternatives in section 1.3.
The Meta-Framework Era
Here's what changed between 2020 and 2025: nobody uses React alone anymore. The framework landscape has split into two layers:
Base frameworks (React, Vue, Svelte): Provide component models and reactivity Meta-frameworks (Next.js, Remix, Astro, Nuxt, SvelteKit): Add routing, data fetching, server rendering, and deployment
React without a meta-framework is like a car engine without a chassis. Technically functional, but you're not going anywhere comfortable. This book teaches React fundamentals first (Chapters 1-15), then Next.js (Chapters 16-22), because you need to understand the engine before you can drive the car.
The Hype Cycle Reality Check
Every few months, a new framework launches with bold claims:
- "10x faster than React!"
- "No virtual DOM overhead!"
- "Zero JavaScript shipped to the client!"
- "Resumability instead of hydration!"
Some of these claims are true. Most are irrelevant to your actual project constraints. Here's what matters more than raw performance:
- Can you hire developers who know it?
- Will it still be maintained in 3 years?
- Does it have libraries for your use case?
- Can you deploy it easily?
- Will your team understand the code in 6 months?
React scores well on all five questions. The new hotness often scores well on none.
Why This Book Focuses on React + Next.js
This book makes an opinionated choice: React with Next.js is the most pragmatic path to building production applications in 2025. Not the most exciting. Not the most innovative. The most pragmatic.
We'll teach you: - React fundamentals that transfer to other frameworks - TypeScript integration that makes large codebases maintainable - Next.js patterns that solve real production problems - When to reach for libraries vs. building yourself - How to debug when things go wrong (they will)
By the end, you'll understand not just how to use React, but when to use it—and when to walk away.
What React actually solves (and what it doesn't)
What React Actually Solves (and What It Doesn't)
Let's start with a concrete problem. Imagine you're building a simple counter interface. Here's how you'd do it with vanilla JavaScript:
<!DOCTYPE html>
<html>
<head>
<title>Vanilla Counter</title>
</head>
<body>
<div id="app">
<h1>Count: <span id="count">0</span></h1>
<button id="increment">Increment</button>
<button id="decrement">Decrement</button>
<button id="reset">Reset</button>
</div>
<script>
let count = 0;
const countElement = document.getElementById('count');
const incrementButton = document.getElementById('increment');
const decrementButton = document.getElementById('decrement');
const resetButton = document.getElementById('reset');
function updateDisplay() {
countElement.textContent = count;
}
incrementButton.addEventListener('click', () => {
count++;
updateDisplay();
});
decrementButton.addEventListener('click', () => {
count--;
updateDisplay();
});
resetButton.addEventListener('click', () => {
count = 0;
updateDisplay();
});
</script>
</body>
</html>
This works perfectly. It's 30 lines of straightforward code. No build tools, no dependencies, no framework. You can open this file directly in a browser.
Now let's add a feature: display the count in three different places (header, sidebar, footer). And make the count turn red when negative, green when positive.
<!DOCTYPE html>
<html>
<head>
<title>Vanilla Counter - Extended</title>
<style>
.positive { color: green; }
.negative { color: red; }
.zero { color: black; }
</style>
</head>
<body>
<header>
<h1>Count: <span id="count-header" class="zero">0</span></h1>
</header>
<main>
<div id="app">
<button id="increment">Increment</button>
<button id="decrement">Decrement</button>
<button id="reset">Reset</button>
</div>
</main>
<aside>
<p>Sidebar count: <span id="count-sidebar" class="zero">0</span></p>
</aside>
<footer>
<p>Footer count: <span id="count-footer" class="zero">0</span></p>
</footer>
<script>
let count = 0;
const countHeader = document.getElementById('count-header');
const countSidebar = document.getElementById('count-sidebar');
const countFooter = document.getElementById('count-footer');
const incrementButton = document.getElementById('increment');
const decrementButton = document.getElementById('decrement');
const resetButton = document.getElementById('reset');
function getColorClass(value) {
if (value > 0) return 'positive';
if (value < 0) return 'negative';
return 'zero';
}
function updateDisplay() {
const colorClass = getColorClass(count);
const countText = count.toString();
// Update header
countHeader.textContent = countText;
countHeader.className = colorClass;
// Update sidebar
countSidebar.textContent = countText;
countSidebar.className = colorClass;
// Update footer
countFooter.textContent = countText;
countFooter.className = colorClass;
}
incrementButton.addEventListener('click', () => {
count++;
updateDisplay();
});
decrementButton.addEventListener('click', () => {
count--;
updateDisplay();
});
resetButton.addEventListener('click', () => {
count = 0;
updateDisplay();
});
</script>
</body>
</html>
Now we're at 70 lines. Notice what happened:
- Manual DOM synchronization: We have to manually update three separate elements
- Repetitive code: The same update logic repeated three times
- Fragile coupling: If we add a fourth display location, we must remember to update
updateDisplay() - Error-prone: Forget to update one element, and your UI is inconsistent
This is the problem React solves: keeping the UI in sync with application state.
The React Solution
Here's the same functionality in React:
// Counter.tsx
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const colorClass = count > 0 ? 'positive' : count < 0 ? 'negative' : 'zero';
return (
<>
<header>
<h1>Count: <span className={colorClass}>{count}</span></h1>
</header>
<main>
<button onClick={() => setCount(count + 1)}>Increment</button>
<button onClick={() => setCount(count - 1)}>Decrement</button>
<button onClick={() => setCount(0)}>Reset</button>
</main>
<aside>
<p>Sidebar count: <span className={colorClass}>{count}</span></p>
</aside>
<footer>
<p>Footer count: <span className={colorClass}>{count}</span></p>
</footer>
</>
);
}
export default Counter;
This is 30 lines—same as our original vanilla version—but it handles the complex case. Notice what changed:
- No manual DOM updates: React handles synchronization automatically
- Single source of truth:
countis defined once, used everywhere - Declarative: We describe what the UI should look like, not how to update it
- Composable: Easy to extract
<CountDisplay value={count} />as a reusable component
What React Actually Solves
React solves one specific problem: keeping the DOM in sync with application state as that state changes over time.
It does this through:
- Declarative UI: You describe what the UI should look like for any given state
- Automatic updates: React figures out what changed and updates only what's necessary
- Component composition: Break complex UIs into reusable pieces
- Unidirectional data flow: Data flows down, events flow up (we'll explore this in Chapter 2)
What React Doesn't Solve
React is not a complete application framework. It doesn't provide:
- Routing: How to handle
/productsvs./products/123(you need React Router or Next.js) - Data fetching: How to load data from APIs (you need fetch + useEffect, or React Query, or Next.js)
- State management: How to share state across distant components (you need Context, Zustand, or similar)
- Server rendering: How to render on the server for SEO and performance (you need Next.js or Remix)
- Build tooling: How to bundle, transpile, and optimize code (you need Vite, Next.js, or similar)
- Styling: How to style components (you need CSS, Tailwind, CSS-in-JS, or CSS Modules)
- Forms: How to handle complex form validation (you need React Hook Form or similar)
- Authentication: How to handle login/logout (you need NextAuth or similar)
This is why we said earlier: nobody uses React alone. You always need additional tools. This book will teach you the pragmatic stack:
- React: Core UI library (Chapters 1-7)
- TypeScript: Type safety (Chapters 8-10)
- Zustand: Global state when needed (Chapter 12)
- React Query: Server state management (Chapter 13)
- React Router: Client-side routing (Chapters 14-15)
- Next.js: Server rendering, routing, data fetching (Chapters 16-22)
- React Hook Form + Zod: Forms and validation (Chapter 6)
- Tailwind CSS: Styling (Chapter 21)
The Mental Model Shift
The hardest part of learning React isn't the syntax—it's the mental model shift from imperative to declarative programming.
Imperative (vanilla JavaScript): 1. Get the element 2. Update its text content 3. Update its class name 4. Repeat for each element
Declarative (React): 1. Describe what the UI should look like given the current state 2. React handles the updates
This shift feels unnatural at first. Your brain wants to reach for document.getElementById(). Resist that urge. Trust that React will handle the updates. By Chapter 3, this will feel natural.
When Vanilla JavaScript Is Better
React adds complexity. Sometimes that complexity isn't justified. Use vanilla JavaScript when:
- The page is mostly static: A blog post with one interactive widget doesn't need React
- You're adding interactivity to an existing site: Sprinkling in React components is possible but awkward
- The interaction is simple: A single dropdown menu doesn't need a framework
- Performance is critical and the page is small: React's runtime has overhead (though it's small)
- You're building a browser extension: Smaller bundle size matters
Use React when:
- The UI has complex state: Multiple pieces of state that affect multiple parts of the UI
- You're building a single-page application: The entire page is dynamic
- You need component reusability: Building a design system or component library
- The team is already using React: Consistency matters more than theoretical purity
- You need server rendering: Next.js makes this trivial
The Honest Truth About React
React is not magic. It's not even particularly innovative anymore—the ideas are 10+ years old. But it's:
- Mature: Most bugs are fixed, most patterns are established
- Pragmatic: Solves real problems without excessive ceremony
- Employable: Knowing React gets you hired
- Ecosystem-rich: Every problem has a library
- Well-documented: Official docs are excellent (as of 2023 rewrite)
Is it the best framework? Depends on your definition of "best." Is it the most pragmatic choice for most projects in 2025? Yes.
Let's move on to when you should choose React vs. alternatives.
When to use React vs. alternatives
When to Use React vs. Alternatives
The framework you choose matters less than you think—and more than you'd hope. Let's cut through the hype and make pragmatic decisions.
The Decision Framework
When choosing a frontend framework, evaluate these factors in order:
- Team expertise: What does your team already know?
- Hiring constraints: Can you hire developers who know this framework?
- Project requirements: What are you actually building?
- Ecosystem needs: Do you need specific libraries?
- Performance requirements: Are you building a content site or an app?
- Deployment constraints: Where will this run?
Notice that "framework performance benchmarks" isn't on the list. That's intentional. Micro-benchmarks rarely matter in real applications.
When to Choose React
Choose React when:
You're building a complex, interactive application - Dashboard with real-time updates - Admin panel with many forms - Social media feed - Collaborative editing tool - E-commerce site with dynamic filtering
You need to hire developers - React has the largest talent pool - Easier to find senior developers - More bootcamp graduates know React - Lower training costs for new hires
You're working with an existing React codebase - Consistency matters more than theoretical purity - Rewriting is almost never worth it - Incremental improvements are safer
You need a mature ecosystem - Every problem has a React solution - Component libraries (shadcn/ui, Radix, Chakra) - State management (Zustand, Redux, Jotai) - Data fetching (React Query, SWR) - Forms (React Hook Form, Formik)
You want the Next.js advantage - Server rendering without complexity - File-based routing - API routes - Image optimization - Deployment to Vercel (one command)
When to Choose Vue
Choose Vue when:
You're a solo developer or small team - Easier learning curve than React - Less boilerplate - Better developer experience out of the box - Single-file components are intuitive
You're building a content-heavy site with some interactivity - Marketing site with interactive demos - Documentation site with live examples - Blog with dynamic features
You value official tooling - Vue CLI is excellent - Vite was created by Vue's author - Vue DevTools are polished - Official router and state management
You're in Asia or Europe - Larger Vue communities in these regions - More local job opportunities - Better local language documentation
When to Choose Svelte
Choose Svelte when:
You're building a small to medium application - Svelte's compiler approach shines here - Less runtime overhead - Smaller bundle sizes
You value developer experience - Less boilerplate than React - More intuitive reactivity - Cleaner syntax - Built-in animations
You're okay with a smaller ecosystem - Fewer libraries available - Smaller community - Less Stack Overflow answers - But SvelteKit is maturing rapidly
Performance is critical - No virtual DOM overhead - Smaller bundle sizes - Faster initial load
When to Choose Angular
Choose Angular when:
You're in a large enterprise - Angular is popular in corporations - Opinionated structure helps large teams - TypeScript-first from day one - Comprehensive official tooling
You need everything in one box - Routing, state management, HTTP client all included - No decision fatigue - Consistent patterns enforced
You're building a long-lived application - Angular's stability is excellent - Breaking changes are rare - Long-term support is guaranteed
You have Java/C# developers - Angular's patterns feel familiar - Dependency injection - Decorators and classes - Strong typing
When to Choose Vanilla JavaScript
Choose vanilla JavaScript when:
You're adding one interactive widget to a static site - A single dropdown menu - An image carousel - A simple form - A modal dialog
The page is mostly static - Blog posts - Marketing pages - Documentation - Landing pages
You're building a browser extension - Bundle size matters - No build step is simpler - Faster development iteration
You're learning web fundamentals - Understanding the DOM is crucial - Framework knowledge builds on vanilla JS - You'll debug frameworks better
The Pragmatic Reality
Here's what actually happens in most projects:
- You inherit an existing codebase: Use whatever it's already using
- Your company has a standard: Use that standard
- You're starting fresh: Choose React + Next.js unless you have a specific reason not to
- You're building a side project: Choose whatever you want to learn
The "Just Use React" Heuristic
If you're unsure, default to React + Next.js because:
- Lowest risk: Most likely to still be maintained in 5 years
- Easiest hiring: Largest talent pool
- Best ecosystem: Most libraries and tools
- Proven at scale: Used by Meta, Netflix, Airbnb, etc.
- Next.js is excellent: Solves most production problems
Is it the most exciting choice? No. Is it the most pragmatic? Yes.
When NOT to Use React
Don't use React when:
You're building a static site - Use Astro, 11ty, or Hugo - Ship zero JavaScript by default - Add interactivity with islands
You need instant interactivity - Use Qwik or Astro with islands - React's hydration has overhead - Content sites suffer most
You're building a mobile app - Use React Native (different from React) - Or use native Swift/Kotlin - Web views are a last resort
You're building a game - Use a game engine (Unity, Godot) - Or vanilla Canvas/WebGL - React's overhead hurts here
You're building a data visualization - Use D3.js directly - Or Observable Plot - React adds unnecessary layers
The Migration Path
If you choose wrong, you can migrate:
React → Next.js: Trivial, Next.js is React React → Vue: Moderate difficulty, similar concepts React → Svelte: Moderate difficulty, different reactivity Vue → React: Moderate difficulty, similar concepts Angular → React: High difficulty, very different patterns Vanilla → React: Easy, incremental adoption possible
The hardest migrations are away from Angular and between fundamentally different paradigms (e.g., React to Solid).
Decision Matrix
Here's a quick reference:
| Project Type | Recommended | Alternative | Avoid |
|---|---|---|---|
| SPA Dashboard | React + Next.js | Vue + Nuxt | Angular (overkill) |
| Marketing Site | Astro | Next.js | React alone |
| E-commerce | Next.js | Remix | Vanilla JS |
| Admin Panel | React + Next.js | Vue + Nuxt | Svelte (ecosystem) |
| Blog | Astro | Next.js | React alone |
| Documentation | Astro | Next.js | Angular |
| Mobile App | React Native | Flutter | React (wrong tool) |
| Browser Extension | Vanilla JS | Svelte | React (bundle size) |
The Bottom Line
For most web applications in 2025, React + Next.js is the pragmatic default. Not because it's perfect, but because it's:
- Mature enough to be stable
- Popular enough to hire for
- Flexible enough to handle most use cases
- Well-supported enough to get help
Choose something else when you have a specific reason, not just because it's newer or claims to be faster.
Now let's actually build something.
Setting up your first React project with Vite
Setting up Your First React Project with Vite
Theory is useful. Working code is better. Let's build something.
Why Vite?
Before we start, let's address the elephant: why Vite and not Create React App (CRA)?
Create React App was the standard way to start React projects from 2016-2023. As of 2025, it's effectively deprecated:
- Slow: Uses Webpack, which is slower than modern alternatives
- Unmaintained: Last major update was years ago
- Bloated: Includes many dependencies you don't need
- Inflexible: Hard to customize without ejecting
Vite (pronounced "veet", French for "fast") is the modern replacement:
- Fast: Uses esbuild for development, Rollup for production
- Simple: Minimal configuration needed
- Modern: Supports ESM, TypeScript, and modern browsers by default
- Flexible: Easy to customize when needed
The React team now recommends using a framework (Next.js, Remix) or Vite for new projects. We'll use Vite for learning React fundamentals (Chapters 1-15), then switch to Next.js for production patterns (Chapters 16-22).
Prerequisites
Before we begin, ensure you have:
- Node.js 18+: Check with
node --version - npm 9+: Check with
npm --version - A code editor: VS Code is recommended (see Appendix A for setup)
- A modern browser: Chrome, Firefox, or Edge with DevTools
If you don't have Node.js, download it from nodejs.org. The LTS (Long Term Support) version is recommended.
Creating Your First React Project
Open your terminal and run:
npm create vite@latest my-first-react-app -- --template react-ts
Let's break down this command:
npm create vite@latest: Uses npm's create command to run the latest Vite scaffolding toolmy-first-react-app: The name of your project folder-- --template react-ts: Uses the React + TypeScript template
You'll see output like this:
Scaffolding project in /Users/you/my-first-react-app...
Done. Now run:
cd my-first-react-app
npm install
npm run dev
Follow those instructions:
cd my-first-react-app
npm install
npm run dev
After a few seconds, you'll see:
VITE v5.0.0 ready in 234 ms
âžś Local: http://localhost:5173/
âžś Network: use --host to expose
âžś press h + enter to show help
Open your browser to http://localhost:5173/. You should see the Vite + React welcome page with a counter button.
Congratulations! You just created your first React application. Let's understand what just happened.
Project Structure
Open the project in your code editor. You'll see this structure:
my-first-react-app/
├── node_modules/ # Dependencies (don't touch)
├── public/ # Static assets
│ └── vite.svg
├── src/ # Your application code
│ ├── assets/
│ │ └── react.svg
│ ├── App.css # Component styles
│ ├── App.tsx # Main component
│ ├── index.css # Global styles
│ └── main.tsx # Application entry point
├── .gitignore # Git ignore rules
├── index.html # HTML entry point
├── package.json # Project metadata and dependencies
├── tsconfig.json # TypeScript configuration
├── tsconfig.node.json # TypeScript config for Node.js
└── vite.config.ts # Vite configuration
Let's examine the key files:
The Entry Point: index.html
Open index.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
Notice two critical lines:
<div id="root"></div>: This is where React will render your application<script type="module" src="/src/main.tsx"></script>: This loads your React code
The React Entry Point: src/main.tsx
Open src/main.tsx:
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>,
)
This file does three things:
- Imports React and ReactDOM: The core libraries
- Imports your App component: The root of your component tree
- Renders the App: Mounts it to the
#rootdiv inindex.html
The React.StrictMode wrapper enables additional development checks. We'll discuss this in Chapter 3.
Your First Component: src/App.tsx
Open src/App.tsx:
import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'
function App() {
const [count, setCount] = useState(0)
return (
<>
<div>
<a href="https://vitejs.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<h1>Vite + React</h1>
<div className="card">
<button onClick={() => setCount((count) => count + 1)}>
count is {count}
</button>
<p>
Edit src/App.tsx
This is a React component. Don't worry about understanding every line yet—we'll cover components in depth in Chapter 2. For now, notice:
- It's a function:
function App() { ... } - It uses state:
const [count, setCount] = useState(0) - It returns JSX: HTML-like syntax that describes the UI
- It's exported:
export default Appso other files can import it
Making Your First Change
Let's modify the component to make it yours. Replace the entire contents of src/App.tsx with:
import { useState } from 'react'
import './App.css'
function App() {
const [count, setCount] = useState(0)
return (
<div className="App">
<h1>My First React App</h1>
<p>This is a counter. Click the button to increment it.</p>
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
<button onClick={() => setCount(0)}>
Reset
</button>
</div>
)
}
export default App
Save the file. Your browser should automatically update—this is Hot Module Replacement (HMR), one of Vite's killer features. You don't need to manually refresh.
Understanding What You Just Built
Let's break down what's happening:
- State declaration:
const [count, setCount] = useState(0) - Creates a piece of state called
count, initialized to0 setCountis a function to update that state-
We'll explore this deeply in Chapter 3
-
Event handler:
onClick={() => setCount(count + 1)} - When the button is clicked, call
setCountwithcount + 1 - React re-renders the component with the new count
-
The UI updates automatically
-
JSX: The HTML-like syntax
- It's not HTML—it's JavaScript that looks like HTML
- Gets compiled to
React.createElement()calls - We'll explore JSX in Chapter 2
The Development Workflow
Your development workflow will be:
- Edit code in your editor
- Save the file
- See changes instantly in the browser (HMR)
- Check the console for errors (we'll set this up next)
No manual refresh needed. No build step during development. This is why Vite is fast.
Setting Up Your Diagnostic Toolkit
Before we go further, let's set up the tools you'll use to debug React applications. These tools are essential—you'll use them in every chapter.
Browser DevTools
Open your browser's DevTools:
- Chrome/Edge: Press
F12orCmd+Option+I(Mac) /Ctrl+Shift+I(Windows) - Firefox: Press
F12orCmd+Option+I(Mac) /Ctrl+Shift+I(Windows)
You should see several tabs: Elements, Console, Sources, Network, etc.
The Console Tab
Click the Console tab. This is where you'll see:
- Errors: Red messages when something breaks
- Warnings: Yellow messages about potential issues
- Logs: Output from
console.log()statements
Try adding a console.log to your component:
import { useState } from 'react'
import './App.css'
function App() {
const [count, setCount] = useState(0)
console.log('App rendered with count:', count)
return (
<div className="App">
<h1>My First React App</h1>
<p>This is a counter. Click the button to increment it.</p>
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
<button onClick={() => setCount(0)}>
Reset
</button>
</div>
)
}
export default App
Save and check the console. You should see:
App rendered with count: 0
Click the increment button. You'll see:
App rendered with count: 0
App rendered with count: 1
This is your first insight into how React works: Every time state changes, the component re-renders. The console.log runs on every render. We'll explore this deeply in Chapter 3.
React Developer Tools
The browser's built-in DevTools are good, but React has its own specialized tools. Install the React Developer Tools extension:
- Chrome: Chrome Web Store - React Developer Tools
- Firefox: Firefox Add-ons - React Developer Tools
- Edge: Edge Add-ons - React Developer Tools
After installing, refresh your React app. You'll see two new tabs in DevTools:
- ⚛️ Components: Inspect the React component tree
- ⚛️ Profiler: Measure component performance
Using React DevTools - Components Tab
Click the ⚛️ Components tab. You'll see your component tree:
â–Ľ App
<div.App>
<h1>
<p>
<button>
<button>
Click on App in the tree. On the right side, you'll see:
Props: (empty for now—we'll cover props in Chapter 2)
Hooks:
- State: 0 (your count state)
Try clicking the increment button in your app. Watch the Hooks section in React DevTools. You'll see the state value update in real-time.
This is your second critical insight: React DevTools shows you the internal state of your components. When debugging, you'll spend a lot of time here.
Using React DevTools - Profiler Tab
Click the ⚛️ Profiler tab. Click the blue record button (circle), then click your increment button a few times, then click stop (square).
You'll see a flame graph showing: - Which components rendered - How long each render took - Why each component rendered
For now, you'll see App rendered multiple times, each taking ~0.1ms. This is normal. We'll use the Profiler extensively in Chapter 25 (Performance Optimization).
The Network Tab
Click the Network tab in your browser's DevTools. Refresh the page. You'll see all the files your app loads:
index.html: The HTML entry pointmain.tsx: Your React code (compiled)App.tsx: Your component (compiled)- Various CSS and asset files
This tab is crucial for debugging data fetching issues. We'll use it extensively in Chapter 4 (Data Fetching) and Chapter 18 (Next.js Data Fetching).
Your Complete Diagnostic Toolkit
You now have four essential tools:
- Browser Console: Errors, warnings, and logs
- React DevTools - Components: Component tree and state inspection
- React DevTools - Profiler: Performance measurement
- Network Tab: HTTP requests and responses
Memorize these keyboard shortcuts:
F12: Open DevToolsCmd/Ctrl + K: Clear consoleCmd/Ctrl + Shift + C: Inspect elementCmd/Ctrl + R: Refresh pageCmd/Ctrl + Shift + R: Hard refresh (clear cache)
Verifying Your Setup
Let's verify everything works. Create a new file src/Greeting.tsx:
// src/Greeting.tsx
interface GreetingProps {
name: string;
}
function Greeting({ name }: GreetingProps) {
console.log('Greeting rendered for:', name);
return <h2>Hello, {name}!</h2>;
}
export default Greeting;
Now update src/App.tsx to use this component:
import { useState } from 'react'
import Greeting from './Greeting'
import './App.css'
function App() {
const [count, setCount] = useState(0)
const [name, setName] = useState('World')
console.log('App rendered with count:', count)
return (
<div className="App">
<h1>My First React App</h1>
<Greeting name={name} />
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Enter your name"
/>
<p>This is a counter. Click the button to increment it.</p>
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
<button onClick={() => setCount(0)}>
Reset
</button>
</div>
)
}
export default App
Save and check your browser. You should see:
- A greeting: "Hello, World!"
- An input field: Type your name and watch the greeting update
- The counter: Still works as before
Now check your diagnostic tools:
Console Verification
Open the Console tab. You should see:
App rendered with count: 0
Greeting rendered for: World
Type in the input field. You'll see:
App rendered with count: 0
Greeting rendered for: W
App rendered with count: 0
Greeting rendered for: Wo
App rendered with count: 0
Greeting rendered for: Wor
Notice: Both App and Greeting re-render on every keystroke. This is normal React behavior. We'll learn to optimize this in Chapter 25.
React DevTools Verification
Open the ⚛️ Components tab. You should see:
â–Ľ App
â–Ľ Greeting
<h2>
<input>
<p>
<button>
<button>
Click on App. In the right panel, you'll see:
Hooks:
- State: 0 (count)
- State: "World" (name)
Click on Greeting. You'll see:
Props:
- name: "World"
Type in the input field and watch the props update in real-time.
TypeScript Verification
Let's verify TypeScript is working. Try passing the wrong type to Greeting:
<Greeting name={123} />
Save the file. You should see a red squiggly line under 123 in your editor, and this error in the terminal:
src/App.tsx:15:23 - error TS2322: Type 'number' is not assignable to type 'string'.
15 <Greeting name={123} />
~~~
This is TypeScript working correctly. It caught a type error before you even ran the code. Change it back to a string:
<Greeting name={name} />
The error disappears. This is why we use TypeScript—it catches bugs at compile time instead of runtime.
Common Setup Issues
If something isn't working, check these common issues:
Issue: Port 5173 is already in use
Error:
Port 5173 is in use, trying another one...
Solution: Either: 1. Stop the other process using port 5173 2. Let Vite use a different port (it will automatically try 5174, 5175, etc.)
Issue: Module not found
Error:
Failed to resolve import "./Greeting" from "src/App.tsx"
Solution: Check:
1. File exists at the correct path
2. File extension is included in import (.tsx)
3. File name matches exactly (case-sensitive)
Issue: React DevTools not showing
Problem: No ⚛️ tabs in DevTools
Solution:
1. Verify the extension is installed
2. Refresh the page
3. Check that you're on localhost:5173 (not file://)
4. Try restarting the browser
Issue: TypeScript errors in editor but code runs
Problem: Red squiggles in VS Code but npm run dev works
Solution: This is normal. TypeScript in your editor and TypeScript in Vite are separate. The editor might be using a different TypeScript version. To fix:
1. Open VS Code command palette (Cmd/Ctrl + Shift + P)
2. Type "TypeScript: Select TypeScript Version"
3. Choose "Use Workspace Version"
Project Scripts
Your package.json includes these scripts:
{
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
}
}
npm run dev: Start development server (what you've been using)npm run build: Build for production (creates optimized files indist/)npm run preview: Preview the production build locally
Try building for production:
npm run build
You'll see output like:
vite v5.0.0 building for production...
âś“ 34 modules transformed.
dist/index.html 0.46 kB │ gzip: 0.30 kB
dist/assets/index-a3b4c5d6.css 1.23 kB │ gzip: 0.64 kB
dist/assets/index-d7e8f9g0.js 143.21 kB │ gzip: 46.12 kB
âś“ built in 1.23s
This creates a dist/ folder with optimized files ready for deployment. We'll cover deployment in Chapter 22.
What You've Learned
You now have:
- A working React development environment with Vite
- Your diagnostic toolkit set up and verified:
- Browser Console for errors and logs
- React DevTools for component inspection
- Network tab for HTTP debugging
- TypeScript for compile-time error checking
- A basic understanding of:
- Project structure
- Component files
- State and props
- How React re-renders
Next Steps
In Chapter 2, we'll dive deep into components and props. You'll learn: - What components actually are - How to pass data between components - Composition patterns - Thinking in React
But before we move on, let's establish one more critical habit: reading error messages.
Learning to Read Error Messages
The most important skill in React development isn't writing code—it's reading error messages. Let's practice.
Create an intentional error. In src/App.tsx, try to use a variable that doesn't exist:
function App() {
const [count, setCount] = useState(0)
console.log(nonExistentVariable) // This will error
return (
// ... rest of component
)
}
Save the file. Check your browser console:
Uncaught ReferenceError: nonExistentVariable is not defined
at App (App.tsx:8:15)
at renderWithHooks (react-dom.development.js:16305:18)
at mountIndeterminateComponent (react-dom.development.js:20074:13)
Let's parse this error systematically:
- Error type:
ReferenceError- trying to use an undefined variable - Error message:
nonExistentVariable is not defined- tells you exactly what's wrong - Location:
at App (App.tsx:8:15)- file, line, and column - Stack trace: Shows the call chain that led to the error
This is the pattern you'll follow for every error: 1. Read the error type 2. Read the error message 3. Find the location 4. Understand the context
Remove the error line and save. The error disappears.
One More Error Pattern
Try this error:
function App() {
const [count, setCount] = useState(0)
return (
<div className="App">
<h1>My First React App</h1>
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
<button onClick={() => setCount(0)}>
Reset
</button>
// Missing closing </div>
)
}
Save the file. You'll see a TypeScript error in your terminal:
src/App.tsx:15:3 - error TS1005: ')' expected.
15 )
~
And a Vite error in your browser:
[plugin:vite:react-babel] Expected corresponding JSX closing tag for <div>
Notice: You got TWO errors: 1. TypeScript caught the syntax error 2. Vite's JSX parser also caught it
This is normal. When you have a syntax error, you'll often see multiple error messages. Always fix the first error first—subsequent errors are often caused by the first one.
Add the closing </div> and save. Both errors disappear.
The Debugging Mindset
As you work through this book, you'll encounter many intentional errors. This is by design. Learning to debug is more valuable than learning to write perfect code.
When you see an error: 1. Don't panic: Errors are data, not failure 2. Read the message: It usually tells you exactly what's wrong 3. Check the location: Go to the file and line number 4. Use your tools: Console, React DevTools, Network tab 5. Isolate the problem: Comment out code until the error disappears 6. Fix incrementally: Make one change at a time
We'll practice this pattern in every chapter.
Your First React Project is Complete
You now have: - A working React development environment - Your diagnostic toolkit configured - A basic understanding of React's rendering model - The ability to read and fix errors
In Chapter 2, we'll build a real component: a User Profile Dashboard. You'll learn component composition, props, and how to think in React.
But first, take a moment to appreciate what you've accomplished. You went from zero to a working React application with TypeScript, hot module replacement, and professional debugging tools. That's not trivial.
Save your work:
git init
git add .
git commit -m "Initial React setup with Vite"
Now you're ready to learn React properly.